package com.sxt.demo.djiapplication.dji

import android.content.Context
import android.text.InputType
import android.view.LayoutInflater
import android.widget.Toast
import android.widget.Toast.LENGTH_LONG
import android.widget.Toast.LENGTH_SHORT
import com.amap.api.maps2d.model.Marker
import com.qx.wz.dj.rtcm.StringUtil
import com.sxt.demo.djiapplication.DjiDemoApplication
import com.sxt.demo.djiapplication.R
import com.sxt.demo.djiapplication.dao.WayPointRecordService
import com.sxt.demo.djiapplication.databinding.GlobalConfigDialogLayoutBinding
import com.sxt.demo.djiapplication.databinding.WaypointConfigDialogLayoutBinding
import com.sxt.demo.djiapplication.entity.WayPointRecord
import com.sxt.demo.djiapplication.entity.WayPointRecordItem
import com.sxt.demo.djiapplication.utils.DisplayUtil
import com.sxt.demo.djiapplication.utils.MCoordinateConverter
import com.sxt.demo.djiapplication.utils.ToastUtils
import com.sxt.demo.djiapplication.view.LoadingView
import com.xuexiang.xui.widget.dialog.materialdialog.MaterialDialog
import dji.common.error.DJIError
import dji.common.mission.waypoint.*
import dji.sdk.mission.waypoint.WaypointMissionOperator
import dji.sdk.sdkmanager.DJISDKManager
import kotlinx.coroutines.*
import java.text.SimpleDateFormat
import java.util.*
import java.util.regex.Matcher
import java.util.regex.Pattern
import kotlin.collections.LinkedHashMap

@Suppress("unused")
class WayPointMissionOperator(
    /**
     * 加载任务位点操作时的上下文, 一般是一个activity
     */
    val context: Context,

    /**
     * 位点任务航向模式
     */
    private val mHeadingMode: WaypointMissionHeadingMode = WaypointMissionHeadingMode.AUTO,

    /**
     * 自动飞行默认速度
     */
    private var mSpeed: Float = 3.0f,

    /**
     * 飞行高度
     */
    private var altitude: Float = 10.0f,

    /**
     * 路点任务完成后的操作
     */
    private val mFinishedAction: WaypointMissionFinishedAction = WaypointMissionFinishedAction.NO_ACTION
) {

    /**
     * 无人机位点任务操作符
     */
    private val wayPointMissionOperator: WaypointMissionOperator by lazy {
        DJISDKManager
            .getInstance()
            .missionControl
            .waypointMissionOperator
    }

    /**
     * 位点集合
     */
    private val wayPointMap: LinkedHashMap<Marker, WayPoint> =
        LinkedHashMap()

    /**
     * 位点集合是否被修改
     */
    private var hasBeenModified = false

    /**
     * 操作监听器
     */
    private val operatorListener = object : DefaultWayPointMissionOperatorListener() {
        override fun onExecutionFinish(error: DJIError?) {
            ToastUtils.show("任务执行完成, 执行结果: ${error?.description ?: "成功"}")
        }
    }

    private val loadingView by lazy { LoadingView(context) }

    /**
     * 到达位点后是否拍照
     */
    private var needTakePicOnWayPoint = false

    /**
     * 全局配置对话框
     */
    private val globalConfigDialog by lazy {
        val inflate = GlobalConfigDialogLayoutBinding.inflate(LayoutInflater.from(context))
        MaterialDialog.Builder(context)
            .customView(inflate.root, true)
            .positiveText(R.string.ensure)
            .negativeText(R.string.cancel)
            .autoDismiss(false)
            .onPositive { dialog, _ ->

                // 全局飞行高度设置
                val inputAltitude = inflate.etAltitude.text.toString()
                val (invalidAltitude, altitude) = getInputAltitude(inputAltitude)
                if (!invalidAltitude) {
                    return@onPositive
                } else {
                    altitude?.let { this.altitude = it }
                }

                // 全局飞行速度控制
                val inputSpeed = inflate.etSpeed.text.toString()
                val (invalidSpeed, speed) = getInputSpeed(inputSpeed)
                if (!invalidSpeed) {
                    return@onPositive
                } else {
                    speed?.let { this.mSpeed = it }
                }

                // 设置完成之后的动作
                when (inflate.actionAfterFinished.checkedRadioButtonId) {
                    // 不采取任何操作
                    R.id.finishNone -> wayPointMissionBuilder.finishedAction(
                        WaypointMissionFinishedAction.NO_ACTION
                    )
                    // 返回出发地
                    R.id.finishGoHome -> wayPointMissionBuilder.finishedAction(
                        WaypointMissionFinishedAction.GO_HOME
                    )
                    // 自动着陆
                    R.id.finishAutoLanding -> wayPointMissionBuilder.finishedAction(
                        WaypointMissionFinishedAction.AUTO_LAND
                    )
                    // 回到第一个位点
                    R.id.finishToFirst -> wayPointMissionBuilder.finishedAction(
                        WaypointMissionFinishedAction.GO_FIRST_WAYPOINT
                    )
                }

                // 设置航向
                when (inflate.heading.checkedRadioButtonId) {
                    //飞机的航向总是在飞行方向。
                    R.id.headingNext -> wayPointMissionBuilder.headingMode(
                        WaypointMissionHeadingMode.AUTO
                    )
                    // 飞机航向将设置为初始起飞航向
                    R.id.headingInitDirec -> wayPointMissionBuilder.headingMode(
                        WaypointMissionHeadingMode.USING_INITIAL_DIRECTION
                    )
                    // 飞机的航向由遥控器控制
                    R.id.headingRC -> wayPointMissionBuilder.headingMode(WaypointMissionHeadingMode.CONTROL_BY_REMOTE_CONTROLLER)
                    // 在两个航路点之间飞行时，飞机的航向将设置为上一个航路点的航向
                    R.id.finishToFirst -> wayPointMissionBuilder.headingMode(
                        WaypointMissionHeadingMode.USING_WAYPOINT_HEADING
                    )
                }

                // 设置到达位点后是否拍照
                when (inflate.takePhotoOnWayPoint.checkedRadioButtonId) {
                    // 不采取任何操作
                    R.id.take_photo_no -> needTakePicOnWayPoint = false
                    // 到达位点后拍照
                    R.id.take_photo_yes -> needTakePicOnWayPoint = true
                }

                configWayPointMission()
                loadingView.dismissLoading()
                ToastUtils.show("设置成功")
                dialog.dismiss()

            }
            .onNegative { dialog, _ -> dialog.dismiss() }
            .autoDismiss(false)
            .build()
    }

    /**
     * 2位小数判断
     */
    private fun isOnlyPointNumber(number: String): Boolean {
        val pattern: Pattern = Pattern.compile("^\\d+\\.?\\d{0,2}$")
        val matcher: Matcher = pattern.matcher(number)
        return matcher.matches()
    }

    /**
     * 获取输入的高度
     */
    private fun getInputAltitude(inputAltitude: String): Pair<Boolean, Float?> {
        if (StringUtil.isNotBlank(inputAltitude)) {
            return if (isOnlyPointNumber(inputAltitude)) {
                Pair(true, inputAltitude.toFloat())
            } else {
                ToastUtils.show("格式有误: 高度格式如: 12, 12.1, 12,23")
                Pair(false, null)
            }
        }
        return Pair(true, null)
    }

    /**
     * 获取输入的速度
     */
    private fun getInputSpeed(inputSpeed: String): Pair<Boolean, Float?> {
        if (StringUtil.isNotBlank(inputSpeed)) {
            return if (isOnlyPointNumber(inputSpeed)) {
                val speed = inputSpeed.toFloat()
                if (speed >= -15 && speed <= 15) {
                    Pair(true, speed)
                } else {
                    ToastUtils.show("格式有误: 速度格式如: 12, 12.1, 12,23且范围在-15-15之间")
                    Pair(false, null)
                }
            } else {
                ToastUtils.show("格式有误: 速度格式如: 12, 12.1, 12,23且范围在-15-15之间")
                Pair(false, null)
            }
        }
        return Pair(true, null)
    }


    /**
     * 位点任务构建器
     */
    private val wayPointMissionBuilder: WaypointMission.Builder by lazy {
        WaypointMission.Builder()
            .finishedAction(mFinishedAction)
            .headingMode(mHeadingMode)
            // 默认自动飞行速度为10m/s
            .autoFlightSpeed(mSpeed)
            // 最大速度15m/s
            .maxFlightSpeed(15f)
            .flightPathMode(WaypointMissionFlightPathMode.NORMAL)
    }

    init {
        /**
         * 初始化位点任务监听器
         */
        wayPointMissionOperator.addListener(operatorListener)
    }

    /**
     * 显示全局位点飞行任务设置窗口
     */
    fun showGlobalConfig() {
        val window = globalConfigDialog.window
        window?.attributes?.let {
            it.width = DisplayUtil.dip2px(360f, DisplayUtil.getScreenDensity(context))
            window.attributes = it
        }
        globalConfigDialog.show()
    }

    /**
     * 将配置及位点加载进位点任务operator
     */
    private fun configWayPointMission() = runBlocking {
        loadingView.setLoadingText("加载位点任务...").showLoading()
        wayPointMap.forEach {
            wayPointMissionBuilder.addWaypoint(it.value.wayPoint)
        }
        wayPointMissionBuilder.waypointCount(wayPointMap.size)
        val error = wayPointMissionOperator.loadMission(wayPointMissionBuilder.build())
        error?.let {
            loadingView.appendLoadingText("加载位点任务提示: ${it.description}")
        } ?: run {
            loadingView.appendLoadingText("加载位点及设置成功")
        }
    }

    /**
     * 添加一个任务位点
     */
    fun addWayPoint(marker: Marker) {
        val position = MCoordinateConverter.toGPSPoint(marker.position)
        // 每个位点的速度
        var speed = mSpeed
        // 每个位点的高度
        var altitude = this.altitude

        // 显示针对某个位点的速度与高度配置对话框
        val inflate = WaypointConfigDialogLayoutBinding.inflate(LayoutInflater.from(context))
        MaterialDialog.Builder(context)
            .title("位点设置")
            .customView(inflate.root, true)
            .positiveText(R.string.ensure)
            .negativeText(R.string.cancel)
            .cancelable(false)
            .autoDismiss(false)
            .onNegative { dialog, _ ->
                // 移除已添加的点
                marker.remove()
                dialog.dismiss()
            }
            .onPositive { dialog, _ ->

                // 位点飞行高度设置
                val inputAltitude = inflate.etAltitude.text.toString()
                val (invalid, tmpAltitude) = getInputAltitude(inputAltitude)
                if (!invalid) {
                    return@onPositive
                } else {
                    tmpAltitude?.let { altitude = it }
                }

                // 位点飞行速度控制
                val inputSpeed = inflate.etSpeed.text.toString()
                val (invalidSpeed, tmpSpeed) = getInputSpeed(inputSpeed)
                if (!invalidSpeed) {
                    return@onPositive
                } else {
                    tmpSpeed?.let { speed = it }
                }

                var needTakePicOnWayPoint = this.needTakePicOnWayPoint

                // 设置到达位点后是否拍照
                when (inflate.takePhotoOnWayPoint.checkedRadioButtonId) {
                    // 不采取任何操作
                    R.id.take_photo_no -> needTakePicOnWayPoint = false
                    // 到达位点后拍照
                    R.id.take_photo_yes -> needTakePicOnWayPoint = true
                }

                val tmp = WayPoint(
                    position.latitude,
                    position.longitude,
                    needTakePicOnWayPoint,
                    altitude,
                    speed,
                    wayPointMap.size + 1
                )
//                val wayPoint =
//                    tmp.wayPoint
                wayPointMap[marker] = tmp
//                wayPointMissionBuilder
//                    .addWaypoint(wayPoint)
//                    .waypointCount(wayPointMap.size)
                hasBeenModified = true
                dialog.dismiss()
            }
            .show()
    }

    /**
     * 添加一组位点集合
     * tip: 为防止顺序错乱, 已有的wayPointMap的数据将会被清除
     */
    fun addWayPointMap(wayPoints: Map<Marker, WayPoint>) {
        clearWayPoints()
        wayPointMap.putAll(wayPoints)
//        wayPoints.forEach {
//            wayPointMissionBuilder.addWaypoint(it.value.wayPoint)
//        }
        hasBeenModified = false
    }

    /**
     * 修改一个位点信息
     * 包括: 高度和速度
     */
    fun modifyWayPoint(marker: Marker) {
        // 显示针对某个位点的速度与高度配置对话框
        val inflate = WaypointConfigDialogLayoutBinding.inflate(LayoutInflater.from(context))
        val wayPoint = wayPointMap[marker]
        // 设置预设值
        inflate.etAltitude.setText("${wayPoint?.getAltitude()}")
        inflate.etSpeed.setText("${wayPoint?.getSpeed()}")
        inflate.takePhotoOnWayPoint.check(
            if (wayPoint?.needTakePic == true) {
                R.id.take_photo_yes
            } else {
                R.id.take_photo_no
            }
        )

        MaterialDialog.Builder(context)
            .title("位点修改")
            .customView(inflate.root, true)
            .positiveText(R.string.ensure)
            .negativeText(R.string.cancel)
            .cancelable(false)
            .autoDismiss(false)
            .onNegative { dialog, _ ->
                dialog.dismiss()
            }
            .onPositive { dialog, _ ->
                // 位点飞行高度设置
                val inputAltitude = inflate.etAltitude.text.toString()
                val (invalid, tmpAltitude) = getInputAltitude(inputAltitude)
                if (!invalid) {
                    return@onPositive
                } else {
                    tmpAltitude?.let {
                        hasBeenModified = true
                        wayPoint?.setAltitude(it)
                    }
                }

                // 位点飞行速度控制
                val inputSpeed = inflate.etSpeed.text.toString()
                val (invalidSpeed, tmpSpeed) = getInputSpeed(inputSpeed)
                if (!invalidSpeed) {
                    return@onPositive
                } else {
                    tmpSpeed?.let {
                        hasBeenModified = true
                        wayPoint?.setSpeed(it)
                    }
                }
                // 设置到达位点后是否拍照
                when (inflate.takePhotoOnWayPoint.checkedRadioButtonId) {
                    // 不采取任何操作
                    R.id.take_photo_no -> wayPoint?.setNeedTakePic(false)
                    // 到达位点后拍照
                    R.id.take_photo_yes -> wayPoint?.setNeedTakePic(true)
                }
                ToastUtils.show(context, "修改成功")
                dialog.dismiss()
            }.show()
    }

    /**
     * 移除一个任务位点
     */
    fun removeWayPoint(marker: Marker) {
        val wayPoint = wayPointMap[marker]
        wayPoint?.let {
//            wayPointMissionBuilder.removeWaypoint(it.wayPoint)
            wayPointMap.remove(marker)
            hasBeenModified = true
        }
    }

    /**
     * 移除所有位点
     */
    fun removeAllWayPoints() {
        clearWayPoints()
    }

    private fun clearWayPoints() {
        wayPointMap.forEach {
            it.key.remove()
//            wayPointMissionBuilder.removeWaypoint(it.value.wayPoint)
        }
        wayPointMap.clear()
    }

    /**
     * 上传位点任务
     */
    fun uploadWayPointMission() {
        if (hasBeenModified) {
            saveWaypointRecords()
        }
        configWayPointMission()
        loadingView.appendLoadingText("上传位点任务...")
        if (wayPointMap.size <= 1) {
            loadingView.appendLoadingText("上传位点任务失败: 请至少添加两个位点")
            loadingView.dismissLoading(2000)
            return
        }
        wayPointMissionOperator.uploadMission {
            it?.let {
                ToastUtils.show(context, "上传位点任务失败: ${it.description}")
                loadingView.appendLoadingText("上传位点任务失败: ${it.description}")
                // 重试
//                wayPointMissionOperator.retryUploadMission(null)
            } ?: run {
                loadingView.appendLoadingText("上传位点任务成功")
                if (hasBeenModified) {
                    saveWaypointRecords()
                }
            }
        }
        loadingView.dismissLoading(1500)
    }

    /**
     * 开启位点任务
     */
    fun startWayPointMission() {
        wayPointMissionOperator.startMission {
            it?.let {
                ToastUtils.show("开启位点任务失败: ${it.description}")
            } ?: run {
                ToastUtils.show("开启位点任务成功")
            }
        }
    }

    /**
     * 停止位点任务
     */
    fun stopWayPointMission() {
        try {
            wayPointMissionOperator.stopMission {
                it?.let {
                    Toast.makeText(context, "停止位点任务失败: ${it.description}", LENGTH_SHORT).show()
                    //                ToastUtils.show("停止位点任务失败: ${it.description}")
                } ?: run {
                    ToastUtils.show("停止位点任务成功")
                }
            }
        } catch (e: Exception) {
            Toast.makeText(context, "exception: ${e.message}", LENGTH_LONG).show()
        }
    }

    /**
     * 界面销毁时调用
     */
    fun destroy() {
        wayPointMissionOperator.removeListener(operatorListener)
    }

    /**
     * 保存位点记录
     */
    private fun saveWaypointRecords() {
        MaterialDialog.Builder(context)
            .title("位点保存")
            .inputType(
                InputType.TYPE_CLASS_TEXT
                        or InputType.TYPE_TEXT_FLAG_CAP_WORDS
            )
            .input(
                context.getString(R.string.pls_input_name),
                "Record_${System.currentTimeMillis()}",
                false
            ) { _, _ -> }
            .negativeText(R.string.cancel)
            .positiveText(R.string.ensure)
            .onPositive { dialog, _ ->
                val saveRs = doSaveWayPointRecords(
                    dialog.inputEditText?.text.toString(),
                    SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS", Locale.CHINA)
                )
                if (saveRs) {
                    hasBeenModified = false
                }
            }
            .canceledOnTouchOutside(false)
            .show()
    }

    private fun doSaveWayPointRecords(name: String, simpleDateFormat: SimpleDateFormat): Boolean {
        val service = DjiDemoApplication.getSingleObj(WayPointRecordService::class.java)
        return service.callInTx { session ->
            val recordTime = name.substringAfter(
                '_',
                "${System.currentTimeMillis()}"
            )
            val id = service.add(
                WayPointRecord(
                    name,
                    simpleDateFormat.format(
                        try {
                            Date(recordTime.toLong())
                        } catch (e: Exception) {
                            Date(System.currentTimeMillis())
                        }
                    )
                )
            )

            wayPointMap.values.forEach {
                val wayPointRecordItem = WayPointRecordItem(
                    id,
                    it.latitude,
                    it.longitude,
                    it.getAltitude(),
                    it.order,
                    it.getSpeed(),
                    it.needTakePic
                )
                session.wayPointRecordItemDao.insert(wayPointRecordItem)
            }
            true
        }
    }

    /**
     * 获取位点对应的order
     */
    fun getWayPointIndex(marker: Marker): Int {
        return wayPointMap[marker]?.order ?: 0
    }
}